﻿using Nemerle;
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;
using Nemerle.Imperative;

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

using Nitra.Declarations;
using Nitra.Serialization2;
using DotNet;

using Ammy.Infrastructure;
using Ammy.Backend;
using Ammy.Symbols;

namespace Ammy.Scopes
{
  public class PriorityScope : Scope
  {
    private _scope : Scope;
    private _priorityAlgorithm : PriorityAlgorithm;
    private _context : AmmyDependentPropertyEvalContext;
    
    public override AllSymbols : Seq[DeclarationSymbol]  { get { _scope.AllSymbols } }
    
    public this(scope : Scope, priorityAlgorithm : PriorityAlgorithm, context : DependentPropertyEvalContext)
    {
      _scope = scope;
      _priorityAlgorithm = priorityAlgorithm;
      _context = context :> AmmyDependentPropertyEvalContext;
    }
    
    private GetSymbolPriority(symbol : DeclarationSymbol) : int
    { 
      _priorityAlgorithm.GetPriority(symbol, _context)
    }
    
    public override FindMany[TSymbol](predicate : Predicate[TSymbol], results : ref LightList[TSymbol]) : void
    {
      _scope.FindMany(predicate, ref results)
    }
    
    public override BindMany[TSymbol](reference : Reference, results : ref LightList[TSymbol]) : void
    {
      _scope.BindMany(reference, ref results);
    }

    public override MakeCompletionList(prefix : string) : Seq[DeclarationSymbol]
    {
      _scope.MakeCompletionList(prefix)
            .OrderByDescending(GetSymbolPriority);
    }

    public override ToString() : string
    {
      <#PriorityScope )#> + _scope
    }

    public override Serialize(writer : BinaryWriter, metadataWriter : MetadataWriter) : void
    {
      metadataWriter.WriteObject(_scope, writer);
    }

    public static Deserialize(reader : BinaryReader, metadataReader : MetadataReader) : this
    {
      def scope = metadataReader.ReadObject(reader);
      PriorityScope(scope, null, null)
    }
  }
  
  public variant PriorityAlgorithm
  {
    | FrameworkElement {
      public override GetPriority(symbol : DeclarationSymbol, context : AmmyDependentPropertyEvalContext) : int
      {        
        match (symbol) {
          | ts is TypeSymbol when ts.IsDescendant(context.Types.FrameworkElement) => 1000 + GetLengthPriority(symbol)
          | _ => GetLengthPriority(symbol)
        }
      }
    }
    
    public GetLengthPriority(symbol : DeclarationSymbol) : int
    {
      20 - symbol.Name.Length
    }
    
    public abstract GetPriority(symbol : DeclarationSymbol, context : AmmyDependentPropertyEvalContext) : int;
  }
}
